// Copyright (C) 2011 and onwards Noam Shazeer and Georges Harik

#include <signal.h>
#include "server.h"

const int32 kBufferSize = 1000000;

bool IsURLSafe(char c) { return isalnum(c); }

string URLEncode(const string & s) {
  ostringstream output;
  for (uint32 i=0; i<s.size(); i++) {
    unsigned char c = s[i];
    if (IsURLSafe(c)) output << c;
    output << "%" << Hex(c/16) << Hex(c%16);
  }
  return output.str();
}
string URLDecode(const string & s) {
  ostringstream output;
  for (uint32 i=0; i<s.size(); i++) {
    unsigned char c = s[i];
    if (c == '+') {
      output << ' ';
      continue;
    } 
    if (c == '%') {
      output << char(HexToInt(s[i+1]) * 16 + HexToInt(s[i+2]));
      i += 2;
      continue;
    } 
    output << c;
  }
  return output.str();
}

string WebRequest::ToString() const {
  ostringstream output;
  output << "PATH: " << path_ << endl;
  forall(run, params_) 
    output << "  " << run->first << " : " << run->second << endl;
  return output.str();
}

void WebServer::Launch() {
  // cout << "::Launch" << endl;
  pthread_t *thread = new pthread_t;
  int ret = pthread_create(thread, NULL, WebServerThread, this);
  if (ret) cout << "Webserver Dead" << endl;
}

void * WebServerThread(void *ws) {
  WebServer * w = (WebServer *)ws;
  w->LoopInternal();
  
  return NULL; }

void WebServer::LoopInternal() {
  // cout << "::LoopInternal" << endl;
  s_socket_ = socket(AF_INET, SOCK_STREAM, 0);
  if (s_socket_ < 0) { cout << "socket error\n"; exit(-1); }
  bzero((char*) &s_address_, sizeof(s_address_));
  s_address_.sin_family = AF_INET;
  s_address_.sin_addr.s_addr = INADDR_ANY;

  while (1) {
    s_address_.sin_port = htons(port_);
    int ret = bind(s_socket_, 
		   (struct sockaddr*) &s_address_, 
		   sizeof(s_address_));
    if (ret < 0) port_++;
    else break;
  }
  cout << "*** USING PORT : " << port_ << endl;
  listen(s_socket_, 5);
  buffer_ = new char[kBufferSize];
  while (1) {
    socklen_t c_length = sizeof(c_address_);
    c_socket_ = accept(s_socket_, 
		       (struct sockaddr *) &c_address_, 
		       &c_length);
    if (c_socket_ < 0) {
      cout << "accept error\n"; exit(-1); }
    // cout << "accepted client!" << endl;
    ReadRequest();
    ParseRequest();
    cout << request_ << endl;
    reply_ = web_backend_->Serve(w_request_);
    Reply();
    close(c_socket_);
  }
}

// Return the offset of the end of the first request from buffer_
bool WebServer::IsCompleteRequest(int so_far) {
  return (string(buffer_ + so_far - 4) == string("\r\n\r\n"));
}

#ifdef __APPLE__
#define MSG_NOSIGNAL 0x2000
#endif

void WebServer::Reply() {
  int32 so_far = 0, this_write = 0;
  while (1) {
    this_write = 
      send(c_socket_, 
	   reply_.c_str() + so_far, 
	   reply_.size() - so_far,
	   MSG_NOSIGNAL);
    if (this_write == -1) {
      cout << "Broken Pipe" << endl;
      break;
    }
    so_far += this_write;
    if (so_far == (int32)reply_.size()) break;
  }
}

void WebServer::ReadRequest() {
  int32 so_far = 0, this_read = 0;
  while ((this_read = read(c_socket_, buffer_ + so_far, kBufferSize))) {
    cout << "read " << this_read << " bytes\n";
    so_far += this_read;
    buffer_[so_far] = '\0';
    if (IsCompleteRequest(so_far)) break;
  }
  istringstream istr(buffer_);
  string url;
  istr >> url >> url;
  request_ = url;
}

// finds the next occurrance of target or '\0'
const char * FindNext(const char * p, char target) {
  const char *ret = p;
  while ((*ret != target) && *ret) ret++;
  return ret;
}

void WebServer::ParseRequest() {
  const char *start = request_.c_str();
  const char *q = FindNext(start, '?');
  w_request_.path_ = string(start, q-start);
  w_request_.params_.clear();
  start = q;
  if (!*start) return;
  start++;
  while (*start) {
    const char *eq = FindNext(start, '=');
    if (!*eq) return;
    string key(start, eq-start);
    start = eq+1;
    const char *amp = FindNext(start, '&');
    string value(start, amp-start);
    w_request_.params_[URLDecode(key)] = URLDecode(value);
    if (!*amp) return;
    start = amp + 1;
  }
}

int webserver_main(int argc, char ** argv) {
  WebBackend w;
  WebServer ws(atoi(argv[1]), &w);
  ws.Launch();
  sleep(10000000);
  return 0;
}
